#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
抖音电商(DY) Redis任务监听器
参考JD架构，实现任务队列处理
"""

import os
import sys
import json
import time
import redis
import pickle
import logging
import traceback
from datetime import datetime
from typing import Dict, List, Any, Optional

# 添加项目根目录到路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from DY.scraper import DYScraper
from DY.config import DYConfig
from selenium.webdriver.common.by import By

class DYRedisMonitor:
    """抖音电商Redis任务监听器"""
    
    def __init__(self):
        """初始化监听器"""
        self.redis_client = None
        self.logger = self._setup_logging()
        
        # Redis配置 - 参考JD配置
        self.REDIS_CONFIG = {
            'host': '8.155.9.53',
            'port': 6300,
            'password': 'jhkdjhkjdhsIUTYURTU_XF6dAk',
            'task_key': ':1:自动获取产品列表'
        }
        
        # API配置
        self.API_CONFIG = DYConfig.API_CONFIG
        
        self.logger.info("DY Redis监听器初始化完成")
    
    def _setup_logging(self):
        """设置日志"""
        logger = logging.getLogger('DYRedisMonitor')
        logger.setLevel(logging.INFO)
        
        if not logger.handlers:
            # 确保输出目录存在
            DYConfig.ensure_output_dir()
            
            # 文件处理器
            log_file = os.path.join(DYConfig.OUTPUT_DIR, 'dy_redis_monitor.log')
            file_handler = logging.FileHandler(log_file, encoding='utf-8')
            file_handler.setFormatter(logging.Formatter(DYConfig.LOG_FORMAT))
            
            # 控制台处理器
            console_handler = logging.StreamHandler()
            console_handler.setFormatter(logging.Formatter(DYConfig.LOG_FORMAT))
            
            logger.addHandler(file_handler)
            logger.addHandler(console_handler)
        
        return logger
    
    def _connect_redis(self) -> bool:
        """连接Redis服务器"""
        try:
            self.redis_client = redis.Redis(
                host=self.REDIS_CONFIG['host'],
                port=self.REDIS_CONFIG['port'],
                password=self.REDIS_CONFIG['password'],
                decode_responses=False  # 保持与JD一致
            )
            
            # 测试连接
            self.redis_client.ping()
            self.logger.info(f"✅ Redis连接成功: {self.REDIS_CONFIG['host']}:{self.REDIS_CONFIG['port']}")
            return True
            
        except redis.AuthenticationError:
            self.logger.error("❌ Redis认证失败，请检查密码")
            return False
        except redis.ConnectionError as e:
            self.logger.error(f"❌ Redis连接失败: {e}")
            return False
        except Exception as e:
            self.logger.error(f"❌ Redis连接异常: {e}")
            return False

    def get_redis_tasks(self) -> List[Dict[str, Any]]:
        """获取Redis中的任务"""
        try:
            if not self.redis_client:
                return []

            # 使用与JD相同的任务键
            task_data = self.redis_client.get(self.REDIS_CONFIG['task_key'])
            if not task_data:
                return []

            # 尝试pickle反序列化（JD格式）
            try:
                tasks = pickle.loads(task_data)

                # 如果pickle返回的是字符串（这是您遇到的情况）
                if isinstance(tasks, str):
                    try:
                        # 将字符串解析为JSON
                        json_tasks = json.loads(tasks)
                        if isinstance(json_tasks, list):
                            return json_tasks
                        elif isinstance(json_tasks, dict):
                            return [json_tasks]
                    except json.JSONDecodeError as e:
                        self.logger.warning(f"Pickle返回的字符串JSON解析失败: {e}")
                        self.logger.debug(f"字符串内容: {tasks[:200]}...")  # 显示前200个字符

                # 如果pickle返回的是列表（正常情况）
                elif isinstance(tasks, list):
                    return tasks

            except Exception as e:
                self.logger.debug(f"Pickle反序列化失败: {e}")

            # 尝试直接JSON反序列化
            try:
                task_str = task_data.decode('utf-8')
                tasks = json.loads(task_str)
                if isinstance(tasks, list):
                    return tasks
                elif isinstance(tasks, dict):
                    return [tasks]
            except Exception as e:
                self.logger.debug(f"JSON反序列化失败: {e}")

            self.logger.warning("无法解析Redis任务数据")
            return []

        except Exception as e:
            self.logger.error(f"获取Redis任务失败: {e}")
            return []
    
    def update_redis_tasks(self, tasks: List[Dict[str, Any]]) -> bool:
        """更新Redis中的任务"""
        try:
            if not self.redis_client:
                return False
            
            if not tasks:
                # 清空任务
                self.redis_client.delete(self.REDIS_CONFIG['task_key'])
                return True
            
            # 使用pickle序列化（与JD保持一致）
            task_data = pickle.dumps(tasks)
            self.redis_client.set(self.REDIS_CONFIG['task_key'], task_data)
            
            return True
            
        except Exception as e:
            self.logger.error(f"更新Redis任务失败: {e}")
            return False
    
    def _validate_task_data(self, task: Dict[str, Any]) -> tuple:
        """验证任务数据完整性"""
        try:
            # 必需字段
            platform = task.get('platform', '').strip()
            store_id = task.get('store_id', '').strip()
            store_url = task.get('store_url', '').strip()
            
            errors = []
            
            # 验证平台
            if not platform:
                errors.append("缺少平台信息 (platform)")
            elif platform != "抖音":
                errors.append(f"平台不匹配，期望'抖音'，实际'{platform}'")
            
            # 验证store_id
            if not store_id:
                errors.append("缺少店铺ID (store_id)")
            
            # 验证store_url
            if not store_url:
                errors.append("缺少店铺URL (store_url)")
            elif "fxg.jinritemai.com" not in store_url:
                errors.append(f"店铺URL格式不正确: {store_url}")
            
            if errors:
                return False, errors
            
            return True, []
            
        except Exception as e:
            return False, [f"任务验证异常: {e}"]
    
    def get_dy_login_mode(self) -> str:
        """获取DY登录模式"""
        print("\n" + "="*50)
        print("🔐 DY登录模式选择")
        print("="*50)
        print("请选择登录方式:")
        print("1. 交互式登录 - 程序协助，需输入验证码")
        print("2. 手动登录 - 完全手动操作")
        print("-"*50)
        
        while True:
            choice = input("请选择登录模式 (1-2): ").strip()
            if choice == "1":
                return "interactive"
            elif choice == "2":
                return "manual"
            else:
                print("❌ 无效选择，请输入 1 或 2")
    
    def manual_dy_login_process(self, scraper) -> bool:
        """手动DY登录流程"""
        try:
            self.logger.info("开始手动DY登录流程")
            
            # 导航到登录页面
            scraper.driver.get(DYConfig.LOGIN_URL)
            time.sleep(3)
            
            print("\n" + "="*60)
            print("👤 手动登录模式")
            print("="*60)
            print("请在浏览器中完成以下操作:")
            print("1. 选择手机号登录")
            print("2. 输入手机号")
            print("3. 获取并输入验证码")
            print("4. 勾选用户协议")
            print("5. 点击登录")
            print("-"*60)
            print("💡 登录成功后通常会跳转到首页，无需额外验证")
            print(f"📍 登录页面: {scraper.driver.current_url}")
            print(f"📄 页面标题: {scraper.driver.title}")
            print("-"*60)
            
            input("✋ 完成登录后，请按 Enter 键继续...")
            
            # 直接标记为登录成功
            print("✅ 手动登录完成，继续后续流程...")
            self.logger.info("✅ 手动登录完成")
            return True
            
        except Exception as e:
            self.logger.error(f"手动登录流程失败: {e}")
            return False
    

    
    def process_dy_task(self, task: Dict[str, Any]) -> bool:
        """处理单个DY任务"""
        try:
            store_id = task['store_id']
            store_url = task['store_url']
            
            self.logger.info(f"🎯 开始处理DY任务: Store ID = {store_id}")
            
            # 初始化爬虫
            scraper = DYScraper(headless=True, debug=False)  # 生产环境使用headless
            
            try:
                scraper._init_driver()
                self.logger.info("🔧 DY爬虫已初始化")
                
                # 获取登录模式（每个任务只选择一次）
                login_mode = self.get_dy_login_mode()
                
                # 登录流程
                self.logger.info("🔐 开始登录流程...")
                
                max_retries = 3
                login_success = False
                
                for retry in range(max_retries):
                    try:
                        if login_mode == "interactive":
                            # 交互式登录
                            if scraper.login_dy("interactive"):
                                login_success = True
                                break
                        else:
                            # 手动登录
                            if self.manual_dy_login_process(scraper):
                                login_success = True
                                break
                        
                        if retry < max_retries - 1:
                            print(f"🔄 登录失败，重试 {retry + 1}/{max_retries}")
                            time.sleep(5)
                    
                    except Exception as e:
                        self.logger.error(f"登录尝试 {retry + 1} 失败: {e}")
                        if retry < max_retries - 1:
                            time.sleep(5)
                
                if not login_success:
                    self.logger.error("❌ 登录失败，跳过任务")
                    return False
                
                self.logger.info("✅ 登录成功")
                
                # 导航到商品管理页面
                if not scraper.navigate_to_shop(store_url):
                    self.logger.error("❌ 导航到商品页面失败")
                    return False
                
                # 获取商品列表
                self.logger.info("📦 开始获取商品列表...")
                products = scraper.get_product_list()
                
                if not products:
                    self.logger.warning("⚠️ 未获取到商品数据")
                    return True  # 空数据也算成功
                
                self.logger.info(f"✅ 获取到 {len(products)} 个商品")
                
                # 获取商品详情
                self.logger.info("🔍 开始获取商品详情...")
                detailed_products = scraper.get_product_details(products)
                
                # 转换为API格式
                self.logger.info("🔄 转换数据格式...")
                api_products = []
                for product in detailed_products:
                    api_product = scraper.convert_to_api_format(product)
                    if api_product:
                        api_products.append(api_product)
                
                # 提交到API
                self.logger.info(f"📤 提交数据到API: {len(api_products)} 个商品")
                if scraper.submit_to_api(api_products, store_id):
                    self.logger.info("✅ 任务处理完成")
                    return True
                else:
                    self.logger.error("❌ API提交失败")
                    return False
                
            finally:
                # 清理资源
                try:
                    scraper.driver.quit()
                except:
                    pass
        
        except Exception as e:
            self.logger.error(f"处理DY任务异常: {e}")
            self.logger.error(f"异常详情: {traceback.format_exc()}")
            return False
    
    def start_monitoring(self):
        """开始监听任务"""
        self.logger.info("🎯 DY Redis任务监听器启动")
        self.logger.info(f"📡 监听Redis服务器: {self.REDIS_CONFIG['host']}:{self.REDIS_CONFIG['port']}")
        self.logger.info(f"🎯 API提交地址: {self.API_CONFIG['base_url']}{self.API_CONFIG['endpoints']['products']}")
        self.logger.info("=" * 60)
        
        try:
            while True:
                # 获取任务
                tasks = self.get_redis_tasks()
                
                if not tasks:
                    self.logger.debug("📭 暂无待处理任务，等待中...")
                    time.sleep(10)
                    continue
                
                self.logger.info(f"📋 发现 {len(tasks)} 个待处理任务")
                
                # 处理任务
                remaining_tasks = []
                
                for i, task in enumerate(tasks):
                    try:
                        # 验证任务数据
                        is_valid, errors = self._validate_task_data(task)
                        if not is_valid:
                            self.logger.error(f"❌ 任务 {i+1} 数据无效:")
                            for error in errors:
                                self.logger.error(f"   - {error}")
                            self.logger.error("程序退出，请修复任务数据")
                            return
                        
                        platform = task['platform']
                        store_id = task['store_id']
                        
                        self.logger.info(f"🔄 处理任务: 平台={platform}, Store ID={store_id}")
                        
                        # 处理任务
                        success = self.process_dy_task(task)
                        
                        if success:
                            self.logger.info(f"✅ 任务处理成功: {store_id}")
                        else:
                            self.logger.error(f"❌ 任务处理失败: {store_id}")
                            # 失败的任务重新加入队列
                            task['retry_count'] = task.get('retry_count', 0) + 1
                            if task['retry_count'] < 3:  # 最多重试3次
                                remaining_tasks.append(task)
                            else:
                                self.logger.error(f"❌ 任务 {store_id} 重试次数超限，丢弃")
                    
                    except Exception as e:
                        self.logger.error(f"处理任务 {i+1} 时发生异常: {e}")
                        self.logger.error(f"异常详情: {traceback.format_exc()}")
                
                # 更新Redis中的任务
                self.update_redis_tasks(remaining_tasks)
                
                if remaining_tasks:
                    self.logger.info(f"📋 剩余 {len(remaining_tasks)} 个任务待重试")
                else:
                    self.logger.info("🎉 所有任务处理完成")
                
                # 短暂等待后继续监听
                time.sleep(5)
        
        except KeyboardInterrupt:
            self.logger.info("📴 收到中断信号，停止监听")
        except Exception as e:
            self.logger.error(f"监听过程出错: {e}")
            self.logger.error(f"异常详情: {traceback.format_exc()}")
    
    def test_connection(self):
        """测试连接"""
        print("🧪 DY Redis监听器连接测试")
        print("=" * 50)
        
        # 测试Redis连接
        if self._connect_redis():
            print("✅ Redis连接: 正常")
            
            # 测试任务获取
            try:
                tasks = self.get_redis_tasks()
                print(f"📊 当前任务数量: {len(tasks)}")
            except Exception as e:
                print(f"❌ 任务获取失败: {e}")
        else:
            print("❌ Redis连接: 失败")
        
        # 测试API连接
        try:
            import requests
            api_url = self.API_CONFIG['base_url'] + self.API_CONFIG['endpoints']['products']
            response = requests.get(api_url, timeout=5)
            print(f"✅ API连接: 正常 (状态码: {response.status_code})")
        except Exception as e:
            print(f"❌ API连接: 失败 - {e}")
        
        print("\n📊 配置信息:")
        print(f"Redis服务器: {self.REDIS_CONFIG['host']}:{self.REDIS_CONFIG['port']}")
        print(f"Redis任务Key: {self.REDIS_CONFIG['task_key']}")
        print(f"API地址: {self.API_CONFIG['base_url']}{self.API_CONFIG['endpoints']['products']}")
        print(f"DY登录URL: {DYConfig.LOGIN_URL}")
        print("✅ 连接测试完成")
    
    def main(self):
        """主菜单"""
        while True:
            print("\n" + "="*40)
            print("📋 DY Redis 任务管理器")
            print("="*40)
            print("1. 查看当前任务")
            print("2. 添加DY测试任务")
            print("3. 清空所有任务")
            print("4. 测试连接")
            print("5. 🎧 启动监听器")
            print("6. 退出")
            print("-"*40)
            
            choice = input("请选择操作 (1-6): ").strip()
            
            if choice == "1":
                self._show_tasks()
            elif choice == "2":
                self._add_test_task()
            elif choice == "3":
                self._clear_tasks()
            elif choice == "4":
                self.test_connection()
            elif choice == "5":
                print("🚀 启动监听器...")
                print("💡 按 Ctrl+C 停止监听")
                if self._connect_redis():
                    self.start_monitoring()
                else:
                    print("❌ Redis连接失败，无法启动监听器")
            elif choice == "6":
                print("👋 再见！")
                break
            else:
                print("❌ 无效选择")
    
    def _show_tasks(self):
        """显示当前任务"""
        if not self._connect_redis():
            return
        
        tasks = self.get_redis_tasks()
        print(f"\n📋 当前任务数量: {len(tasks)}")
        
        for i, task in enumerate(tasks, 1):
            platform = task.get('platform', '未知')
            store_id = task.get('store_id', '未知')
            retry_count = task.get('retry_count', 0)
            store_url = task.get('store_url', '未知')
            
            # 验证任务
            is_valid, errors = self._validate_task_data(task)
            status = "✅" if is_valid else "❌"
            
            print(f"  {i}. 平台: {platform}, Store ID: {store_id}, 重试: {retry_count} {status}")
            print(f"     URL: {store_url}")
            
            if not is_valid:
                for error in errors:
                    print(f"     ❌ {error}")
    
    def _add_test_task(self):
        """添加测试任务"""
        if not self._connect_redis():
            return
        
        print("\n📝 添加DY测试任务")
        store_url = input("请输入商品管理页面URL (默认: 测试URL): ").strip()
        if not store_url:
            store_url = "https://fxg.jinritemai.com/ffa/goods/list"  # 默认测试URL
        
        import uuid
        test_task = {
            "platform": "抖音",
            "store_id": str(uuid.uuid4()),
            "store_url": store_url,
            "retry_count": 0
        }
        
        tasks = self.get_redis_tasks()
        tasks.append(test_task)
        
        if self.update_redis_tasks(tasks):
            print("✅ 测试任务添加成功")
        else:
            print("❌ 测试任务添加失败")
    
    def _clear_tasks(self):
        """清空所有任务"""
        if not self._connect_redis():
            return
        
        confirm = input("⚠️ 确认清空所有任务？(y/N): ").strip().lower()
        if confirm == 'y':
            if self.update_redis_tasks([]):
                print("✅ 所有任务已清空")
            else:
                print("❌ 清空任务失败")
        else:
            print("📋 操作已取消")

if __name__ == "__main__":
    monitor = DYRedisMonitor()
    monitor.main() 